package auth

import (
	"log"
	"strings"

	"gitee.com/go-course/go11/devcloud-mini/mcenter/apps/token"
	"gitee.com/go-course/go11/devcloud-mini/mcenter/clients/rpc"
	"github.com/emicklei/go-restful/v3"
	"github.com/infraboard/mcube/exception"
	"github.com/infraboard/mcube/http/restful/response"
)

// 中间件构造函数
func NewAuthFilter(service string) restful.FilterFunction {
	return NewAuth(rpc.C(), service).Filter
}

func NewAuth(c *rpc.Client, service string) *Auth {
	return &Auth{
		c:       c,
		service: service,
	}
}

type Auth struct {
	c       *rpc.Client
	service string
}

// 业务功能
//  1. 需要rpc客户端, 采用全局变量来进行设计
//  2. rpc.ValidateToken 来进行鉴权
//     2.1 token: cookie(web)/header(Authtication)/query(websocket)参数
//     2.2 校验 成功 pass, 失败: 中断流程 返回认证错误(401)
func (a *Auth) Filter(
	req *restful.Request,
	w *restful.Response,
	next *restful.FilterChain) {
	authEnabled := false
	// 判断路由是否开启了认证
	// 获取当前匹配到路由, 读取路由的Meta信息
	meta := req.SelectedRoute().Metadata()
	authv := meta["auth"]
	if authv != nil {
		if v, ok := authv.(bool); ok {
			authEnabled = v
		}
	}

	if authEnabled {
		tk, err := a.auth(req)
		if err != nil {
			response.Failed(w, exception.NewUnauthorized("未认证, %s", err))
			return
		}

		// 再知道用户身份的前提下 才能进行鉴权
		permEnabled := false
		permv := meta["permission"]
		if permv != nil {
			if v, ok := permv.(bool); ok {
				permEnabled = v
			}
		}
		if permEnabled {
			resource, action := "", ""
			// 根据用户的角色来判断 用户是否可以访问该服务的接口
			resourcev := meta["resource"]
			if resourcev != nil {
				resource = resourcev.(string)
			}

			actionv := meta["action"]
			if actionv != nil {
				action = actionv.(string)
			}

			err := tk.HasPerm(a.service, resource, action)
			if err != nil {
				response.Failed(w, exception.NewPermissionDeny(err.Error()))
				return
			}
		}

		// 认证通过后需要把认证通过的信息放到上下文中去
		req.SetAttribute(token.CONTEXT_ATTRIBUTE_KEY, tk)
	}

	// Gin Next
	next.ProcessFilter(req, w)
}

func (a *Auth) auth(req *restful.Request) (*token.Token, error) {
	tk := req.HeaderParameter(token.HeaderAuthKey)
	if tk != "" {
		tkFormat := strings.Split(tk, " ")
		if len(tkFormat) > 1 {
			tk = tkFormat[1]
		}
	}
	// 兼容Cookie
	if tk == "" {
		ck, err := req.Request.Cookie(token.CookieAuthKey)
		if err != nil {
			log.Println(err)
		} else {
			tk = ck.Value
		}
	}

	// 2.2 Token校验
	return a.c.Token().ValidateToken(
		req.Request.Context(),
		token.NewValidateTokenRequest(tk),
	)
}
